package gateway02.config;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import common05.common.Results;
import common05.common.Token;
import common05.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimNames;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.Charset;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class GatewayConfig implements GlobalFilter, Ordered {

    @Autowired
    private JwtDecoder jwtDecoder;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getPath().toString();
        log.info("接口访问路径  path=[{}]", path);

        //本次路径是否是Security中可以未登录就能访问的url中的其中一个,默认为否
        boolean pathPermitAll = false;

        /*判断本次访问的path是否符合Security中可以未登录就能访问的url中的其中一个*/
        for (String urlPrefix : Token.JWT_PERMIT_ALL_URL_PREFIX) {
            if (path.startsWith(urlPrefix)) {
                pathPermitAll = true;
                break;
            }
        }

        if (pathPermitAll) {
            //如果是Security中允许未登录就能访问的url，直接放行
            return chain.filter(exchange);
        }

        //如果不是Security中允许未登录就能访问的url，需要判断jwt本身是否正确，并且还需要判断是否具有权限
        String tokenRes = hasPermission(exchange, path);

        if (Token.JWT_SUCCESS.equals(tokenRes)) {
            //jwt和权限判断成功，直接放行
            return chain.filter(exchange);
        } else {
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.OK);
            response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
            Results results;
            if (Token.JWT_USER_SCOPE_ERROR.equals(tokenRes)) {
                results = Results.forbidden();
            } else if (Token.JWT_NULL_OR_ERROR.equals(tokenRes)) {
                results = Results.unauthorized();
            } else {
                results = Results.error();
            }
            DataBuffer buffer = response.bufferFactory().wrap(JSONUtil.toJsonStr(results).getBytes(Charset.forName("UTF-8")));
            return response.writeWith(Mono.just(buffer));
        }
    }

    private String hasPermission(ServerWebExchange exchange, String path) {
        List<String> tokenList = exchange.getRequest().getHeaders().get(Token.TOKEN_NAME);
        String token = null;
        if (tokenList == null || tokenList.size() != 1 || !tokenList.get(0).startsWith(Token.JWT_PREFIX)) {
            //token为空或者错误
            return Token.JWT_NULL_OR_ERROR;
        } else {
            token = tokenList.get(0).substring(7);
        }

        Jwt jwt = jwtDecoder.decode(token);
        if (jwt == null) {
            //jwt为空
            return Token.JWT_NULL_OR_ERROR;
        } else {
            String iss = jwt.getClaim(JwtClaimNames.ISS);
            if (!Token.JWT_ISS.equals(iss)) {
                return Token.JWT_NULL_OR_ERROR;
            }
            String sub = jwt.getClaim(JwtClaimNames.SUB);
            if (StringUtils.isNull(sub)) {
                return Token.JWT_NULL_OR_ERROR;
            }
            String tokenRedis = stringRedisTemplate.opsForValue().get(Token.getTokenRedisKey(sub));
            if (StringUtils.isNull(tokenRedis) || !token.equals(tokenRedis)) {
                return Token.JWT_NULL_OR_ERROR;
            }

            Instant expiresAt = jwt.getExpiresAt();
            if (expiresAt == null || Instant.now().isAfter(expiresAt)) {
                //已经过期 也就是jwt不合法
                return Token.JWT_NULL_OR_ERROR;
            }

            Map<String, Object> claimsObj = jwt.getClaims();
            if (claimsObj == null || claimsObj.size() <= 0) {
                //jwt不对，不合法
                return Token.JWT_NULL_OR_ERROR;
            }
            JSONObject claims = new JSONObject(claimsObj);


            String scope = claims.getStr("scope");
            if (StringUtils.isNull(scope)) {
                //用户没有任何权限，无法访问接口
                return Token.JWT_USER_SCOPE_ERROR;
            } else {
                String[] scopeArray = scope.split(" ");
                List<String> scopeList = Arrays.asList(scopeArray);
                if (scopeList.contains(path)) {
                    //说明包含该权限，可以访问
                    exchange.getRequest().mutate().headers(httpHeaders -> httpHeaders.set("userId", sub));
                    return Token.JWT_SUCCESS;
                } else {
                    //说明不包含该权限，不能访问
                    return Token.JWT_USER_SCOPE_ERROR;
                }
            }
        }

    }


    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}
